#ifndef UTILS_H
#define UTILS_H

#include <cmath>
#include <map>
#include <random>
#include <string>
#include <vector>

#include "base.h"

// 断层夹角范围，超过 165° 的断层点可以忽略 (cos(165°) = -0.9659258262890682)
constexpr double cos_fault_angle_threshold = -0.9659258262890682;

enum PointType {
    // 上表面点
    PT_UPPER_SURFACE = 0,
    // 下表面点
    PT_LOWER_SURFACE,
    // 断层点
    PT_FAULT,
    // 地层点
    PT_LAYER,
    // 内部点
    PT_INNER,
    // 其他点
    PT_OTHER
};

enum ExpandCellType {
    // 储层，原始网格
    RESERVOIR = 0,
    // 同层围岩
    SIDESBURDEN,
    // 上覆岩石
    OVERSIDES,
    // 上覆围岩
    OVERBURDEN,
    // 下覆岩石
    UNDERSIDES,
    // 下覆围岩
    UNDERBURDEN,
    // 其他
    OTHER
};

typedef struct GridIndex {
    INDEX x;
    INDEX y;
    INDEX z;

    GridIndex() {}
    GridIndex(INDEX x, INDEX y, INDEX z): x(x), y(y), z(z) {}
} GridIndex;

// 读入优化的缓冲区
struct IOBuffer {
    std::string filename;
    char* buffer;
    int buffer_size, buffer_pos;
    FILE* file;
    bool file_finished; // 当前是否读取完毕或遇到错误

    IOBuffer(std::string filename): filename(filename) {
        buffer = new char[IO_BUFFER_SIZE];
        buffer_size = 0;
        buffer_pos = 0;
        file_finished = false;
        file = fopen(filename.c_str(), "r");
        if(!file) {
            file_finished = true;
        }
    }
    
    ~IOBuffer() {
        delete[] buffer;
        fclose(file);
    }
    
    // 读取一个字符，返回第一个读取的非 0 字符，返回 0 表示文件读取完毕
    inline char getChar() {
        if(file_finished) {
            return 0;
        }
        while(true) {
            if(buffer_pos == buffer_size) {
                buffer_size = fread(buffer, 1, IO_BUFFER_SIZE, file);
                if(!buffer_size) { // 文件读取完毕
                    file_finished = true;
                    return 0;
                } 
                buffer_pos = 0;
            }
            while(buffer_pos < buffer_size) {
                if(buffer[buffer_pos]) { // 读取成功
                    return buffer[buffer_pos++];
                }
                buffer_pos++; // 读取到 0，继续读取下一个字符
            }
        }
        return 0;
    }

    // 是否为空字符 : ' ', '\n', '\t', '\r'
    inline bool isEmptyChar(char c) {
        return c == ' ' || c == '\n' || c == '\t' || c == '\r';
    }

    // 是否是数字字符
    inline bool isDigit(char c) {
        return c >= '0' && c <= '9';
    }

    // 读取一个整数，读取失败时返回 0，成功时返回整数后一个字符
    inline char getInteger(INDEX &val) {
        char c = getChar();
        int sign = 1;
        while(isEmptyChar(c)) {
            c = getChar();
        }
        if(c == 0) {
            return 0;
        }
        if(c == '-') {
            sign = -1;
            c = getChar();
        }
        val = 0;
        while(isDigit(c)) {
            val = val * 10 + c - '0';
            c = getChar();
        }
        val *= sign;
        return c;
    }

    // 读取一个浮点数，读取失败时返回 0，成功时返回浮点数后一个字符
    inline char getFloat(double &val) {
        char c = getChar();
        int sign = 1;
        while(isEmptyChar(c)) {
            c = getChar();
        }
        if(c == 0) {
            return 0;
        }
        if(c == '-') {
            sign = -1;
            c = getChar();
        }
        val = 0;
        while(isDigit(c)) {
            val = val * 10 + c - '0';
            c = getChar();
        }
        if(c == '.') {
            c = getChar();
            double dec = 0.1;
            while(isDigit(c)) {
                val += dec * (c - '0');
                dec *= 0.1;
                c = getChar();
            }
        }
        val *= sign;
        return c;
    }

    // 读取一个字符串，读取失败时返回 0，成功时返回字符串后一个字符
    inline char getString(std::string &val) {
        char c = getChar();
        while(isEmptyChar(c)) {
            c = getChar();
        }
        if(c == 0) {
            return false;
        }
        val = "";
        while(c && !isEmptyChar(c)) {
            val.push_back(c);
            c = getChar();
        }
        return c;
    }

    // 读取一个字符串，读取失败或读到 terminate 字符时返回 0，成功时返回字符串后一个字符
    inline char getStringTerminate(std::string &val, char terminate) {
        char c = getChar();
        while(isEmptyChar(c) && c != terminate) {
            c = getChar();
        }
        if(c == 0 || c == terminate) {
            return false;
        }
        val = "";
        while(c && !isEmptyChar(c)) {
            val.push_back(c);
            c = getChar();
        }
        return c;
    }

    // 读取表达式 INTEGER*double，读取失败时返回 0，成功时返回表达式后一个字符
    inline char getExp(INDEX &int_val, double &float_val) {
        char c = getInteger(int_val);
        while(c && c != '*') {
            c = getChar();
        }
        if(!c) {
            return false;
        }
        c = getFloat(float_val);
        if(!c) {
            return false;
        }
        return c;
    }
};

// check 2 float numbers are equal
inline bool isEqual(double a, double b) {
    return std::abs(a - b) < EPS; // different points!
}

inline int randomMinusOneZeroOne(std::mt19937 &gen) {
    // 生成 0 ~ 2 的均匀整数，再减 1 得到 -1, 0, 1
    static thread_local std::uniform_int_distribution<int> dis(0, 2);
    return dis(gen) - 1;
}

inline int searchPoint1D(const std::vector<double> &val, double target) {
    for (int i = 0; i < val.size() - 1; i++) {
        if ((target - val[i]) * (target - val[i + 1]) <= 0) {
            return i;
        }
    }
    return -1; // not found
}

#endif // UTILS_H
